<?php
/**
 * Created by 无所谓已
 * OCRValidator.php
 * OCR字幕验证服务类
 * Time: 2025/01/27
 */

namespace app\service;

class OCRValidator
{
    /**
     * OCR工具类型
     */
    const OCR_TESSERACT = 'tesseract';
    const OCR_PADDLEOCR = 'paddleocr'; // 预留，未来可扩展

    /**
     * 检测OCR工具是否可用
     *
     * @param string $ocrType OCR工具类型
     * @return bool
     */
    public function isOCRAvailable($ocrType = self::OCR_TESSERACT)
    {
        static $cache = [];
        
        if (isset($cache[$ocrType])) {
            return $cache[$ocrType];
        }

        $available = false;

        switch ($ocrType) {
            case self::OCR_TESSERACT:
                $available = $this->isTesseractAvailable();
                break;
            case self::OCR_PADDLEOCR:
                // 预留，未来可扩展
                $available = false;
                break;
        }

        $cache[$ocrType] = $available;
        return $available;
    }

    /**
     * 检测Tesseract OCR是否可用
     *
     * @return bool
     */
    private function isTesseractAvailable()
    {
        // 检查tesseract命令是否存在
        $cmd = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' 
            ? 'where tesseract 2>nul' 
            : 'which tesseract 2>/dev/null';
        
        exec($cmd, $output, $returnCode);
        
        if ($returnCode !== 0) {
            return false;
        }

        // 检查中文语言包是否可用
        return $this->isChineseLanguagePackAvailable();
    }

    /**
     * 检测中文语言包是否可用
     *
     * @return bool
     */
    private function isChineseLanguagePackAvailable()
    {
        // 检查简体中文语言包
        $cmd = "tesseract --list-langs 2>&1";
        exec($cmd, $output, $returnCode);
        
        if ($returnCode !== 0) {
            return false;
        }

        $langs = implode(' ', $output);
        // 检查是否包含中文语言包（chi_sim, chi_tra, chinese_simplified等）
        $chineseLangs = ['chi_sim', 'chi_tra', 'chinese_simplified', 'chinese_traditional', 'chi', 'zh'];
        
        foreach ($chineseLangs as $lang) {
            if (stripos($langs, $lang) !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * 使用OCR识别截图中的中文字幕
     *
     * @param string $imageFile 截图文件路径
     * @param string $ocrType OCR工具类型
     * @return array 识别结果 ['has_chinese' => bool, 'text' => string, 'confidence' => float]
     */
    public function validateScreenshot($imageFile, $ocrType = self::OCR_TESSERACT)
    {
        $result = [
            'has_chinese' => false,
            'text' => '',
            'confidence' => 0.0,
            'ocr_type' => $ocrType,
            'available' => false
        ];

        // 检查OCR工具是否可用
        if (!$this->isOCRAvailable($ocrType)) {
            $result['available'] = false;
            return $result;
        }

        $result['available'] = true;

        switch ($ocrType) {
            case self::OCR_TESSERACT:
                return $this->validateWithTesseract($imageFile);
            case self::OCR_PADDLEOCR:
                // 预留，未来可扩展
                return $result;
            default:
                return $result;
        }
    }

    /**
     * 使用Tesseract OCR识别截图中的中文字幕
     *
     * @param string $imageFile 截图文件路径
     * @return array 识别结果
     */
    private function validateWithTesseract($imageFile)
    {
        $result = [
            'has_chinese' => false,
            'text' => '',
            'confidence' => 0.0,
            'ocr_type' => self::OCR_TESSERACT,
            'available' => true
        ];

        if (!file_exists($imageFile)) {
            return $result;
        }

        // 获取图片尺寸，用于确定字幕区域（通常在底部）
        $imageInfo = @getimagesize($imageFile);
        if (!$imageInfo) {
            return $result;
        }

        $width = $imageInfo[0];
        $height = $imageInfo[1];

        // 字幕通常在画面底部20-30%的区域
        // 使用ImageMagick裁剪字幕区域（如果可用），否则识别整个图片
        $subtitleRegion = $this->extractSubtitleRegion($imageFile, $width, $height);
        
        if ($subtitleRegion) {
            // 使用裁剪后的区域进行OCR
            $tempImage = $subtitleRegion;
        } else {
            // 使用原图进行OCR
            $tempImage = $imageFile;
        }

        // 尝试多种中文语言包
        $chineseLangs = ['chi_sim+chi_tra', 'chi_sim', 'chi_tra', 'chi'];
        
        foreach ($chineseLangs as $lang) {
            // 使用Tesseract OCR识别
            // 使用PSM模式7（单一文本行），适合识别字幕
            $cmd = sprintf(
                'tesseract "%s" stdout -l %s --psm 7 2>&1',
                $tempImage,
                $lang
            );

            exec($cmd, $output, $returnCode);

            if ($returnCode === 0 && !empty($output)) {
                $text = trim(implode(' ', $output));
                
                // 检测是否包含中文字符
                if ($this->containsChinese($text)) {
                    $result['has_chinese'] = true;
                    $result['text'] = $text;
                    $result['confidence'] = $this->calculateConfidence($text);
                    
                    // 清理临时文件
                    if ($subtitleRegion && $subtitleRegion !== $imageFile) {
                        @unlink($subtitleRegion);
                    }
                    
                    return $result;
                }
            }
        }

        // 清理临时文件
        if ($subtitleRegion && $subtitleRegion !== $imageFile) {
            @unlink($subtitleRegion);
        }

        return $result;
    }

    /**
     * 提取字幕区域（画面底部）
     *
     * @param string $imageFile 原图路径
     * @param int $width 图片宽度
     * @param int $height 图片高度
     * @return string|false 临时裁剪图片路径，失败返回false
     */
    private function extractSubtitleRegion($imageFile, $width, $height)
    {
        // 检查ImageMagick是否可用
        $cmd = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' 
            ? 'where convert 2>nul' 
            : 'which convert 2>/dev/null';
        
        exec($cmd, $output, $returnCode);
        
        if ($returnCode !== 0) {
            return false; // ImageMagick不可用，返回false
        }

        // 字幕区域：画面底部30%的区域
        $cropHeight = intval($height * 0.3);
        $cropY = $height - $cropHeight;

        $tempPath = runtime_path() . '/temp/';
        if (!is_dir($tempPath)) {
            mkdir($tempPath, 0755, true);
        }

        $tempImage = $tempPath . 'ocr_region_' . uniqid() . '.png';

        // 使用ImageMagick裁剪字幕区域
        $cmd = sprintf(
            'convert "%s" -crop %dx%d+0+%d "%s" 2>&1',
            $imageFile,
            $width,
            $cropHeight,
            $cropY,
            $tempImage
        );

        exec($cmd, $output, $returnCode);

        if ($returnCode === 0 && file_exists($tempImage)) {
            return $tempImage;
        }

        return false;
    }

    /**
     * 检测文本是否包含中文字符
     *
     * @param string $text 文本内容
     * @return bool
     */
    private function containsChinese($text)
    {
        // 使用Unicode范围检测中文字符
        // 中文字符范围：\x{4e00}-\x{9fa5}（基本汉字）
        // 还包括：\x{3400}-\x{4dbf}（扩展A）、\x{20000}-\x{2a6df}（扩展B）等
        return preg_match('/[\x{4e00}-\x{9fa5}]/u', $text) === 1;
    }

    /**
     * 计算OCR识别置信度
     *
     * @param string $text 识别出的文本
     * @return float 置信度（0-1）
     */
    private function calculateConfidence($text)
    {
        if (empty($text)) {
            return 0.0;
        }

        // 简单计算：中文字符占比
        $totalChars = mb_strlen($text, 'UTF-8');
        if ($totalChars === 0) {
            return 0.0;
        }

        $chineseChars = 0;
        $length = mb_strlen($text, 'UTF-8');
        for ($i = 0; $i < $length; $i++) {
            $char = mb_substr($text, $i, 1, 'UTF-8');
            if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $char)) {
                $chineseChars++;
            }
        }

        return min(1.0, $chineseChars / $totalChars);
    }

    /**
     * 批量验证截图（用于选择最佳截图）
     *
     * @param array $screenshotFiles 截图文件路径数组
     * @param string $ocrType OCR工具类型
     * @return array 最佳截图索引和验证结果
     */
    public function findBestScreenshot($screenshotFiles, $ocrType = self::OCR_TESSERACT)
    {
        $bestIndex = -1;
        $bestResult = null;
        $maxConfidence = 0.0;

        foreach ($screenshotFiles as $index => $file) {
            if (!file_exists($file)) {
                continue;
            }

            $result = $this->validateScreenshot($file, $ocrType);
            
            if ($result['has_chinese'] && $result['confidence'] > $maxConfidence) {
                $maxConfidence = $result['confidence'];
                $bestIndex = $index;
                $bestResult = $result;
            }
        }

        return [
            'best_index' => $bestIndex,
            'best_result' => $bestResult,
            'max_confidence' => $maxConfidence
        ];
    }
}

